Mammon --- Traslation by Little-John --- Issue 1 (Oct/Nov 98)

"SMC Techniques: The Basics"...................................mammon_
"CAM Tecniche: Le basi"........................................mammon_

 Uno dei vantaggi della programmazione in assembler � che hai piene facolt� di controllo sull'applicazione:
la 'ginnastica binaria' del codice di un virus dimostra ci� pi� di ogni altra cosa. Uno dei "trucchi" utilizzati 
dai virus, che ha avuto poi seguito negli schemi di protezione dei programmi, � il codice automodificante
(SMC = self-modifyng code).

 In quest'articolo non tratter� di virus polimorfici o di motori di mutazione (mutation engines); non analizzer�
nessuno schema di protezione in particolare, non considerer� trucchi anti-debugger/anti-disassembler, e non
affronter� l'argomento della PIQ (Prefetch Instruction Queue, ndt). Questo articolo � solamente una prima
trattazione riguardante il codice automodificante, per coloro a cui il concetto risulta nuovo e da
implementare.

 Episodio 1: Cambiamento di opcode (opcode alteration)
 -----------------------------------------------------
 Una delle forme pi� pure del codice automodificante � il cambiare il valore di una istruzione  prima che sia
eseguita... a volte come il risultato di una comparazione, e a volte per  nascondere il codice da occhi curiosi.
Questa tecnica segue essenzialmente questo schema:
	mov reg1, codice-da-sostituire
	mov [indir-su-cui-scrivere], reg1
 in cui 'reg1' pu� essere qualsiasi registro, e '[indir-su-cui-scrivere]' dovrebbe essere un   puntatore
all'indirizzo da cambiare. Nota che il 'codice-da-sostituire' dovrebbe essere una  istruzione in formato
esadecimale, ma posizionando il codice da qualche altra parte nel  programma -- in una subroutine non
chiamata, o in un segmento diverso -- � possibile  semplicemente trasferire il codice compilato da una
locazione ad un'altra attraverso  l'indirizzamento indiretto, come segue:
          call changer
          mov dx, offset [string]     ;questo sar� eseguito ma ignorato
label:    mov ah, 09                  ;questo non sar� mai eseguito
          int 21h                     ;questo chiuder� il programma
          ....
changer:  mov di, offset to_write     ;carica l'indirizzo del codice da scrivere in DI
          mov byte ptr [label], [di]  ;scrivi il codice nella locazione 'label'
          ret                         ;ritorna dalla chiamata
to_write: mov ah, 4Ch                 ;codice di fine programma (int 21, ah=4c ndt)

questa piccola routine far� chiudere il programma, anche se in un disassembler essa all'inizio sembra
essere una semplice routine di scrittura di stringa. Nota che combinando l'indirizzamento con dei loops,
intere subroutine -- anche programmi -- possono essere sovrascritti, e il codice da scrivere -- che pu� essere
presente nel programma come dati -- pu� essere criptato con un semplice XOR per farlo sfuggire da un
disassembler.

Il seguente � un programma in assembler per dimostrare il cambiamento dal "vivo" del codice; esso chiede
all'utente una password, poi cambia la stringa da scrivere a seconda che la password sia corretta o meno.
; smc1.asm ==================================================================
.286
.model small
.stack 200h
.DATA
;buffer for Keyboard Input, formatted for easy reference:
MaxKbLength  db 05h
KbLength     db 00h
KbBuffer     dd 00h

;strings: nota che la password non � criptata, ma potrebbe esserlo
szGuessIt        db     'Care to guess the super-secret password?',0Dh,0Ah,'$'
szString1        db     'Congratulations! You solved it!',0Dh,0Ah, '$'
szString2        db     'Ah, damn, too bad eh?',0Dh,0Ah,'$'
secret_word      db     "this"

.CODE
;===========================================
start:
        mov     ax,@data                ; setta il registro di segmento
        mov     ds, ax                  ; uguale alla direttiva "assume"
        mov     es, ax
        call Query                      ; chiede all'utente la password
        mov     ah, 0Ah                 ; funzione DOS 'Ricevi input dall'utente'
        mov     dx, offset MaxKbLength  ; inizio del buffer
        int     21h
        call Compare                    ; confronta la password e cambia il codice
exit:
        mov ah,4ch                      ; funzione 'Chiudi al DOS'
        int 21h
;===========================================
Query            proc
        mov  dx, offset szGuessIt       ; stringa di richiesta password
        mov  ah, 09h                    ; funzione 'visualizza stringa'
        int  21h
        ret
Query            endp
;===========================================
Reply            proc
PatchSpot:
        mov  dx, offset szString2       ; stringa 'hai fallito'
        mov  ah, 09h                    ; funzione 'visualizza stringa'
        int  21h
        ret
Reply            endp
;===========================================
Compare            proc
        mov     cx, 4                   ; num. di bytes nella password
        mov     si, offset KbBuffer     ; inizio del buffer di input della password
        mov     di, offset secret_word  ; indirizzo della password corretta
        rep cmpsb                       ; confronto password
        or cx, cx                       ; sono uguali?
        jnz     bad_guess               ; no, non applicare il patch
        mov word ptr cs:PatchSpot[1], offset szString1  ;cambia in GoodString
bad_guess:
        call Reply                      ; output della stringa per visualizzare il risultato
        ret
Compare            endp
end     start
; EOF =======================================================================


Episodio 2: Encryption (la traduzione di questo termine � proprio brutta, encriptazione, quindi ho preferito
lasciare l'originale inglese)
-------------------------------------------------
Senza dubbio l'encryption � la forma pi� comune di codice automodificante utilizzato oggigiorno. E' utilizzata
dai packers e dagli exe-encriptors o per comprimere o per nascondere codice, dai virus per rendere oscuri i
propri contenuti, dagli schemi di protezione per nascondere dati. La forma base dell'encription pu� essere:
        mov reg1, indir-da-sovrascrivere
        mov reg2, [reg1]
        manipola reg2
        mov [reg1], reg2
dove 'reg1' sarebbe il registro contenente l'indirizzo (l'offset) della locazione da sovrascrivere, e 'reg2' un
registro temporaneo che carica i contenuti del primo e poi li modifica attraverso operazioni matematiche
(ROL) oppure logiche (XOR). L'indirizzo da cambiare � caricato in reg1, il suo contenuto � poi modificato
all'interno di reg2, ed infine riscritto nella locazione originale ancora contenuta in reg1.

Il programma della sezione precedente pu� essere modificato in modo tale che esso decripti la password
sovrascrivendola (in tal modo questa rimane decriptata finch� il programma non � terminato) prima
cambiando la 'parola segreta' come segue:
secret_word      db     06Ch, 04Dh, 082h, 0D0h

e poi cambiando la routine di confronto, Compare, per cambiare la locazione della 'secret_word' nel
segmento dati:
;===========================================
magic_key        db     18h, 25h, 0EBh, 0A3h ;non molto sicura!

Compare            proc    		     ;Passo 1: decripta la password
        mov     al, [magic_key]              ; metti il byte1 della maschera XOR in al
        mov     bl, [secret_word]            ; metti il byte1 della password in bl
        xor     al, bl
        mov     byte ptr secret_word, al     ; cambia il byte1 della password
        mov     al, [magic_key+1]            ; metti il byte2 della maschera XOR in al
        mov     bl, [secret_word+1]          ; metti il byte2 della password in bl
        xor     al, bl
        mov     byte ptr secret_word[1], al  ; cambia il byte2 della password
        mov     al, [magic_key+2]            ; metti il byte3 della maschera XOR in al
        mov     bl, [secret_word+2]          ; metti il byte3 della password in bl
        xor     al, bl
        mov     byte ptr secret_word[2], al  ; cambia il byte3 della password
        mov     al, [magic_key+3]            ; metti il byte4 della maschera XOR in al
        mov     bl, [secret_word+3]          ; metti il byte4 della password in bl
        xor     al, bl
        mov     byte ptr secret_word[3], al  ; cambia il byte4 della password
        mov     cx, 4   		     ;Passo 2: Comfonta le passwords...nessun
;cambiamento da qui in poi
        mov     si,offset KbBuffer
        mov     di, offset secret_word
        rep     cmpsb
        or      cx, cx
        jnz     bad_guess
        mov     word ptr cs:PatchSpot[1], offset szString1
bad_guess:
        call Reply
        ret
Compare            endp

Nota l'aggiunta della locazione della 'magic_key' (chiave magica) che contiene la maschera XOR per la
password. Tutto ci� potrebbe essere stato eseguito in maniera pi� sofisticata con un loop, ma con soli 4 byte
il codice sopra velocizza il tempo di debugging (e, inoltre, il tempo di scrittura della articolo ( e della
traduzione, ndt !)). Nota come la password � caricata, XORata, e riscritta un byte la volta; utilizzando codice a
32-bit, l'intera password (dword) pu� esser scritta, XORata e riscritta in un sol colpo.

Episodio 3: Giocherellando con lo stack
---------------------------------------
 Questo � un trucco che ho imparato mentre decompilavo del codice di SunTzu. Ci� che accade qui �
abbastanza interessante: lo stack � spostato nel segmento codice del programma, cosicch� il top dello stack
punta al primo indirizzo da essere modificato (che, inoltre, dovrebbe essere uno vicinissimo alla fine del
programma per il modo in cui lo stack funziona); il byte a questo indirizzo � POPato in un registro,
manipolato, e PUSHato indietro nella sua locazione originale. Lo stack pointer (SP) � poi decrementato in
modo che l'indirizzo successivo da essere modificato (1 byte pi� basso in memoria) � ora nel top dello stack.

 In pi�, i byte sono XORati con una porzione del codice stesso del programma, anche per camuffare il valore
della maschera XOR. Nel codice seguente, ho scelto di utilizzare i byte dallo Start: (200h quando � compilato)
fino a -- ma non includendolo -- Exit: (214h quando � compilato; Exit-1=213h).
Comunque, come nel codice originale di SunTzu ho mantenuto la sequenza "inversa" della maschera di XOR
sicch� il byte 213h � il primo byte della maschera di XOR, e il byte 200h ne � l'ultimo. Dopo alcuni
esperimenti ho capito che questo era il modo pi� facile per sincronizzare una patch -- o un editor
esadecimale -- al codice che manipola lo stack; dal momento che lo stack si muove all'indietro (uno stack
che si sposta in avanti � pi� un problema che una soluzione), utilizzando una maschera XOR "inversa" che
permetta a tutti e due i puntatori di file in un patcher di essere INCati o DECati in sincronia.

 Come mai questa � una issue? A differenza dei due esempi precedenti, la seguente non contiene una
versione criptata del codice da modificare. Questa contiene giusto il code di origine che, quando compilato,
risulta essere nei byte non criptati che sono elaborati attraverso la routine di XOR, criptati, ed infine eseguiti
(che, se hai seguito il discorso, si dimostrer� subito di non esser buono... in ogni caso � un ottimo metodo
per far crashare la VM di DOS (Virtual Machine, ndt)).

 Una volta che il programma � compilato bisogna o cambiare i byte da decriptare a mano, o scrivere un
patcher che faccia ci� per te. La prima � pi� conveniente, l'ultima � pi� sicura e diventa una necessit� se hai
intenzione di conservare il codice. Nell'esempio seguente ho incluso 2 CCh (int 3) nel codice prima e dopo la
fine dei byte da decriptare; un patcher deve solo ricercare questi due valori, contare i byte nel mezzo, ed
infine eseguire lo XOR con i byte tra 200h e 213h.

 Ancora una volta, questo esempio � la continuazione di quello precedente. In questo ho scritto una routine
per decriptare per intero la  routine 'Compare' della sezione precedente XORandola con i byte compresi tra
'Start' e 'Exit'. Ci� � eseguito settando il segmento di stack come segmento di codice, poi settando il
puntatore di stack uguale all'ultimo indirizzo di codice (il pi� alto) da modificare. Un byte � POPato dallo
stack (es. la sua locazione originale), XORato, e PUSHato indietro nella sua posizione originale. Il byte
successivo � caricato decrementando il puntatore di stack. Quando tutto il codice � decriptato, il controllo �
restituito alla appena decriptata routine 'Compare' e l'esecuzione continua normalmente.

;===========================================
magic_key        db     18h, 25h, 0EBh, 0A3h

Compare            proc
         mov cx, offset EndPatch[1]    ;inizio dell'indiriz-da-sovrascriv + 1
         sub cx, offset patch_pwd      ;fine dell'indiriz-da-sovrascriv
         mov ax, cs
         mov dx, ss                    ;salva lo stack segment -- importante!
         mov ss, ax                    ;setta lo stack segment come code segment
         mov bx, sp                    ;salva lo stack pointer
         mov sp, offset EndPatch       ;inizio dell'indiriz-da-sovrascriv
         mov si, offset Exit-1         ;inizio dell'indiriz della maschera XOR
XorLoop:
         pop ax                        ;prendi il byte-da-patchare in AL
         xor al, [si]                  ;XORa al con la XorMask
         push ax                       ;scrivi il byte-to-patch in memoria
         dec sp                        ;carica il successivo byte-da-patchare
         dec si                        ;carica il successivo byte della maschera XOR
         cmp si, offset Start          ;fine della maschera XOR?
         jae GoLoop                    ;Se No, continua
         mov si, offset Exit-1         ;reinizializza la maschera XOR
GoLoop:
         loop XorLoop                  ;XORa il byte successivo
         mov sp, bx                    ;ripristina lo stack pointer
         mov ss, dx                    ;ripristina lo stack segment
         jmp    patch_pwd
         db     0CCh,0CCh              ;Identifcation mark: START
patch_pwd:                             ;Nessun cambiamento da qui
        mov     al, [magic_key]
        mov     bl, [secret_word]
        xor     al, bl
        mov     byte ptr secret_word, al
        mov     al, [magic_key+1]
        mov     bl, [secret_word+1]
        xor     al, bl
        mov     byte ptr secret_word[1], al
        mov     al, [magic_key+2]
        mov     bl, [secret_word+2]
        xor     al, bl
        mov     byte ptr secret_word[2], al
        mov     al, [magic_key+3]
        mov     bl, [secret_word+3]
        xor     al, bl
        mov     byte ptr secret_word[3], al
;compare password
        mov     cx, 4
        mov     si, offset KbBuffer
        mov     di, offset secret_word
        rep cmpsb
        or cx, cx
        jnz     bad_guess
        mov word ptr cs:PatchSpot[1], offset szString1
bad_guess:
        call Reply
        ret
Compare            endp
EndPatch:
        db 0CCh, 0CCh                  ;Identification Mark: END

 Questo genere di programmi � davvero difficile da debuggare. Per testarlo, ho sostituito 'xor al, [si]' prima
con 'xor al,00', che non encripta ed � utile per cercercare eventuali errori nel codice, e poi con 'xor al, EBh',
che mi ha permesso di verificare che venivano criptati i byte corretti (non fa mai male dare una controllatina,
dopotutto).

 Episodio 4: Conclusione
------------------------
 Tutto ci� dovrebbe dare le basi sul codice automodificante. Ogni programma che utilizza tali tecniche sar�
pieno di insidie.

 La cosa pi� importante � avere il programma completamente funzionante prima di iniziare a sovrascrivere
parti del suo codice. Inoltre, crea sempre una applicazione che esegua l'inverso di ogni routine di
decriptazione/criptazione -- non solo per velocizzare la compilazione e il test automatizzando l'encriptazione
di codice che sar� decriptato durante l'esecuzione, ma anche per disporre di un buono strumento per
controlli utilizzando un disassemblatore (es. encripta il codice, dissasemblalo, decripta il codice,
disassemblalo, confrontalo). Infatti, � una buona idea mantenere la porzione di codice automodificante del
tuo programma in un eseguibile diverso e testarlo sulla versione definitiva, finch� tutti i bug sono eliminati
dalla routine di decriptazione, e solo allora aggiungi la routine di decriptazione al codice finale. I segni di
riconoscimento CCh (codemarks?) sono anch'essi estremamente utili.

 Infine, esegui il debug con il debug.com per applicazioni DOS -- il debugger � veloce, piccolo, e se crasha
hai solo perso una finestra di DOS.

 Esempi pi� complessi di codice automodificante pu� esser trovato nel codice di Dark Angel, il motore
Rhince, o in qualsiasi motore di mutazione utilizzato nei virus polimorfici. Si ringrazia Sun-Tzu per la tecnica
dello stack usata nella su applicazione ghf-crackme.
----------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------
		**********************************************
		******	Little-John Little-Addiction	******
		******		   Start		******
		**********************************************
		**********************************************


 Le tecniche per scrivere codice automodificante sono molto utili, specialmente quando sono poco leggibili o
interpretabili con un disassemblatore. Una maniera non contemplata da +mammon in questo suo articolo
riguarda l'uso dell'istruzione STOSB/W. Guardate queste 13 righe di codice:

1	mov di, cs:[offset_locazione_da_sovrascrivere]
2	push ds		; salviamo DS nello stack
3	shl ds,4	; operiamo su DS in modo tale da farlo diventare un op-code valido
4	push cx		; questo valore sar� recuperato nell'istruzione #11
5	mov ds, cs:[offset_locazione_da_cui_attingere]
6	cld
7	mov ax,ds	; ax = istruzione da sovrascrivere, di = locazione da sovrascrivere
8	STOSW		; scrive ax in [di] ed incrementa di
9	mov ax,bx+ds	; sposta in ax il valore bx+ds
10	STOSW		; scrive ax in [di] ed incrementa di
11	pop ax		; ax=cx
12	STOSW		; scrive ax in [di] ed incrementa di
13	pop ds		; recuperiamo ds
...

 La 'locazione_da_sovrascrivere' conterr� quindi le nuove istruzioni passate volta per volta da ax. Ho ripetuto
per 3 volte l'istruzione STOSW per far notare come il valore in ax pu� esser caricato in diversi modi a proprio
piacimento.

		**********************************************
		******	Little-John Little-Addiction	******
		******		   End			******
		**********************************************
		**********************************************